Add GtkActionClass::get_submenu() vfunc: actions providing a menu item or
authorEmmanuele Bassi <ebassi@gnome.org>
Thu, 15 Mar 2007 19:33:57 +0000 (19:33 +0000)
committerEmmanuele Bassi <ebassi@src.gnome.org>
Thu, 15 Mar 2007 19:33:57 +0000 (19:33 +0000)
2007-03-15  Emmanuele Bassi  <ebassi@gnome.org>

* gtk/gtkaction.[ch]: Add GtkActionClass::get_submenu() vfunc:
actions providing a menu item or a menu tool button with already
a submenu should return the GtkMenu widget.

* gtk/gtkuimanager.c (update_node): If an action provides its
own submenu, use it instead of adding an empty one

* gtk/gtkrecentaction.[ch]: Add GtkRecentAction, an action
implementing the GtkRecentChooser interface for displaying the
list of recently used files into menus and toolbars generated
using GtkUIManager. (#338843)

* gtk/Makefile.am:
* gtk/gtk.h:
* gtk/gtk.symbols: Add GtkRecentAction API to the build.

* tests/testactions.c: Exercise the GtkRecentAction API.

svn path=/trunk/; revision=17524

ChangeLog
gtk/Makefile.am
gtk/gtk.h
gtk/gtk.symbols
gtk/gtkaction.c
gtk/gtkaction.h
gtk/gtkactiongroup.c
gtk/gtkrecentaction.c [new file with mode: 0644]
gtk/gtkrecentaction.h [new file with mode: 0644]
gtk/gtkuimanager.c
tests/testactions.c

index a049c13f5b74570e77444d3c4748cfd546bcf760..50c4cdf5cdab79cdf15234bda0a08b3a2c5eea31 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2007-03-15  Emmanuele Bassi  <ebassi@gnome.org>
+
+       * gtk/gtkaction.[ch]: Add GtkActionClass::get_submenu() vfunc:
+       actions providing a menu item or a menu tool button with already
+       a submenu should return the GtkMenu widget.
+
+       * gtk/gtkuimanager.c (update_node): If an action provides its
+       own submenu, use it instead of adding an empty one
+
+       * gtk/gtkrecentaction.[ch]: Add GtkRecentAction, an action
+       implementing the GtkRecentChooser interface for displaying the
+       list of recently used files into menus and toolbars generated
+       using GtkUIManager. (#338843)
+
+       * gtk/Makefile.am:
+       * gtk/gtk.h:
+       * gtk/gtk.symbols: Add GtkRecentAction API to the build.
+
+       * tests/testactions.c: Exercise the GtkRecentAction API.
+
 2007-03-15  Chris Wilson  <chris@chris-wilson.co.uk>
 
        * gtk/gtkicontheme.c (ensure_valid_themes), (rescan_themes),
@@ -25,6 +45,7 @@
 
 2007-03-15  Emmanuele Bassi  <ebassi@gnome.org>
 
+>>>>>>> .r17523
        * gtk/gtkrecentchooserprivate.h:
        * gtk/gtkrecentchooserutils.c: Move filtering of the recent
        files list into the shared implementation; do the filtering
index 82c4afd712d63b563fbd81bb65d31f5811e55552..ce5d48f369e0de0f899e1a487ca78608fe125f7a 100644 (file)
@@ -250,6 +250,7 @@ gtk_public_h_sources =          \
        gtkradiotoolbutton.h    \
        gtkrange.h              \
        gtkrc.h                 \
+       gtkrecentaction.h       \
        gtkrecentchooser.h      \
        gtkrecentchooserdialog.h \
        gtkrecentchoosermenu.h  \
@@ -509,6 +510,7 @@ gtk_base_c_sources =            \
        gtkrange.c              \
        gtkrbtree.c             \
        gtkrc.c                 \
+       gtkrecentaction.c       \
        gtkrecentchooserdefault.c \
        gtkrecentchooserdialog.c \
        gtkrecentchoosermenu.c  \
index 487f5eae1395185a9887a0562ba7e1d602bb9786..1fa2d0c8f0b72c854baf20b4d0460887d878d442 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
 #include <gtk/gtkradiotoolbutton.h>
 #include <gtk/gtkrange.h>
 #include <gtk/gtkrc.h>
+#include <gtk/gtkrecentaction.h>
 #include <gtk/gtkrecentchooser.h>
 #include <gtk/gtkrecentchooserdialog.h>
 #include <gtk/gtkrecentchoosermenu.h>
index 1eedabd27b1ae20fa3499d63815a4b038800f950..f66898460aa552ad77024180b822a4ce0c5753f1 100644 (file)
@@ -3030,6 +3030,16 @@ gtk_rc_style_unref
 #endif
 #endif
 
+#if IN_HEADER(__GTK_RECENT_ACTION_H__)
+#if IN_FILE(__GTK_RECENT_ACTION_C__)
+gtk_recent_action_get_type G_GNUC_CONST
+gtk_recent_action_new
+gtk_recent_action_new_for_manager
+gtk_recent_action_set_show_numbers
+gtk_recent_action_get_show_numbers
+#endif
+#endif
+
 #if IN_HEADER(__GTK_RECENT_CHOOSER_H__)
 #if IN_FILE(__GTK_RECENT_CHOOSER_C__)
 gtk_recent_chooser_set_show_private
index 93e1484a89bd63c54b24e4d56d096521c149072c..ea3fe8f44a7d707568056b11ceb726ce2ab368b1 100644 (file)
@@ -147,10 +147,11 @@ static void gtk_action_sync_tooltip     (GtkAction      *action,
 
 static GtkWidget *create_menu_item    (GtkAction *action);
 static GtkWidget *create_tool_item    (GtkAction *action);
-static void       connect_proxy       (GtkAction     *action,
-                                      GtkWidget     *proxy);
+static void       connect_proxy       (GtkAction *action,
+                                      GtkWidget *proxy);
 static void       disconnect_proxy    (GtkAction *action,
                                       GtkWidget *proxy);
+
 static void       closure_accel_activate (GClosure     *closure,
                                          GValue       *return_value,
                                          guint         n_param_values,
@@ -184,6 +185,7 @@ gtk_action_class_init (GtkActionClass *klass)
 
   klass->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
   klass->toolbar_item_type = GTK_TYPE_TOOL_BUTTON;
+  klass->get_submenu = NULL;
 
   g_object_class_install_property (gobject_class,
                                   PROP_NAME,
@@ -1774,5 +1776,28 @@ gtk_action_disconnect_accelerator (GtkAction *action)
                                action->private_data->accel_closure);
 }
 
+/**
+ * gtk_action_get_submenu:
+ * @action: a #GtkAction
+ *
+ * If @action provides a #GtkMenu widget as a submenu for the menu
+ * item or the toolbar item it creates, this function returns that
+ * widget.
+ *
+ * Return value: the menu item provided by the action, or %NULL.
+ *
+ * Since: 2.12
+ */
+GtkWidget *
+gtk_action_get_submenu (GtkAction *action)
+{
+  g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
+
+  if (GTK_ACTION_GET_CLASS (action)->get_submenu)
+    return GTK_ACTION_GET_CLASS (action)->get_submenu (action);
+
+  return NULL;
+}
+
 #define __GTK_ACTION_C__
 #include "gtkaliasdef.c"
index e1f3b128788bbb5ef76af41eab1de433427acbe3..41a0e422a75f6bf6e66085912aef2a51f9d368a6 100644 (file)
@@ -73,8 +73,9 @@ struct _GtkActionClass
   void       (* disconnect_proxy)   (GtkAction *action,
                                     GtkWidget *proxy);
 
+  GtkWidget *(* get_submenu)        (GtkAction *action);
+
   /* Padding for future expansion */
-  void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
   void (*_gtk_reserved4) (void);
@@ -109,6 +110,7 @@ void         gtk_action_connect_accelerator    (GtkAction     *action);
 void         gtk_action_disconnect_accelerator (GtkAction     *action);
 G_CONST_RETURN gchar *gtk_action_get_accel_path (GtkAction     *action);
 GClosure    *gtk_action_get_accel_closure      (GtkAction     *action);
+GtkWidget *  gtk_action_get_submenu            (GtkAction     *action);
 
 /* protected ... for use by child actions */
 void         gtk_action_block_activate_from    (GtkAction     *action,
index 70a45ee84643fc129619734d429a2d0ec2645254..56b6589614c93a18675d4be4faef44dc8c3898fe 100644 (file)
@@ -913,7 +913,7 @@ gtk_action_group_add_toggle_actions_full (GtkActionGroup             *action_gro
       g_object_unref (action);
     }
 
-    shared_data_unref (shared_data);
+  shared_data_unref (shared_data);
 }
 
 /**
diff --git a/gtk/gtkrecentaction.c b/gtk/gtkrecentaction.c
new file mode 100644 (file)
index 0000000..eb70be2
--- /dev/null
@@ -0,0 +1,811 @@
+/* GTK - The GIMP Toolkit
+ * Recent chooser action for GtkUIManager
+ *
+ * Copyright (C) 2007, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gtkintl.h"
+#include "gtkrecentaction.h"
+#include "gtkimagemenuitem.h"
+#include "gtkmenutoolbutton.h"
+#include "gtkrecentchooser.h"
+#include "gtkrecentchoosermenu.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkprivate.h"
+#include "gtkalias.h"
+
+#define FALLBACK_ITEM_LIMIT     10
+
+#define GTK_RECENT_ACTION_GET_PRIVATE(obj)      \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj),    \
+         GTK_TYPE_RECENT_ACTION,                \
+         GtkRecentActionPrivate))
+
+struct _GtkRecentActionPrivate
+{
+  GtkRecentManager *manager;
+  guint manager_changed_id;
+
+  guint show_numbers   : 1;
+
+  /* RecentChooser properties */
+  guint show_private   : 1;
+  guint show_not_found : 1;
+  guint show_tips      : 1;
+  guint show_icons     : 1;
+  guint local_only     : 1;
+
+  gint limit;
+
+  GtkRecentSortType sort_type;
+  GtkRecentSortFunc sort_func;
+  gpointer sort_data;
+  GDestroyNotify data_destroy;
+
+  GtkRecentFilter *current_filter;
+
+  GSList *choosers;
+  GtkRecentChooser *current_chooser;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_SHOW_NUMBERS
+};
+
+static void gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentAction,
+                         gtk_recent_action,
+                         GTK_TYPE_ACTION,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+                                                gtk_recent_chooser_iface_init));
+
+static inline void
+recent_chooser_set_property (GtkRecentAction *action,
+                             const gchar     *property_name,
+                             const GValue    *value)
+{
+  GSList *proxies, *l;
+
+  proxies = gtk_action_get_proxies (GTK_ACTION (action));
+  for (l = proxies; l != NULL; l = l->next)
+    {
+      GObject *proxy = l->data;
+
+      g_object_set_property (proxy, property_name, value);
+    }
+  g_slist_free (proxies);
+}
+
+static gboolean
+gtk_recent_action_set_current_uri (GtkRecentChooser  *chooser,
+                                   const gchar       *uri,
+                                   GError           **error)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = action->priv;
+  GSList *l;
+
+  for (l = priv->choosers; l; l = l->next)
+    {
+      GtkRecentChooser *recent_chooser = l->data;
+
+      if (!gtk_recent_chooser_set_current_uri (recent_chooser, uri, error))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gchar *
+gtk_recent_action_get_current_uri (GtkRecentChooser *chooser)
+{
+  GtkRecentAction *recent_action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = recent_action->priv;
+
+  if (priv->current_chooser)
+    return gtk_recent_chooser_get_current_uri (priv->current_chooser);
+
+  return NULL;
+}
+
+static gboolean
+gtk_recent_action_select_uri (GtkRecentChooser  *chooser,
+                              const gchar       *uri,
+                              GError           **error)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = action->priv;
+  GSList *l;
+
+  for (l = priv->choosers; l; l = l->next)
+    {
+      GtkRecentChooser *recent_chooser = l->data;
+
+      if (!gtk_recent_chooser_select_uri (recent_chooser, uri, error))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+gtk_recent_action_unselect_uri (GtkRecentChooser *chooser,
+                                const gchar      *uri)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = action->priv;
+  GSList *l;
+
+  for (l = priv->choosers; l; l = l->next)
+    {
+      GtkRecentChooser *chooser = l->data;
+      
+      gtk_recent_chooser_unselect_uri (chooser, uri);
+    }
+}
+
+static void
+gtk_recent_action_select_all (GtkRecentChooser *chooser)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_action_unselect_all (GtkRecentChooser *chooser)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+
+static GList *
+gtk_recent_action_get_items (GtkRecentChooser *chooser)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = action->priv;
+
+  return _gtk_recent_chooser_get_items (chooser,
+                                        priv->current_filter,
+                                        priv->sort_func,
+                                        priv->sort_data);
+}
+
+static GtkRecentManager *
+gtk_recent_action_get_recent_manager (GtkRecentChooser *chooser)
+{
+  return GTK_RECENT_ACTION_GET_PRIVATE (chooser)->manager;
+}
+
+static void
+gtk_recent_action_set_sort_func (GtkRecentChooser  *chooser,
+                                 GtkRecentSortFunc  sort_func,
+                                 gpointer           sort_data,
+                                 GDestroyNotify     data_destroy)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
+  GtkRecentActionPrivate *priv = action->priv;
+  
+  if (priv->data_destroy)
+    {
+      priv->data_destroy (priv->sort_data);
+      priv->data_destroy = NULL;
+    }
+      
+  priv->sort_func = NULL;
+  priv->sort_data = NULL;
+  
+  if (sort_func)
+    {
+      priv->sort_func = sort_func;
+      priv->sort_data = sort_data;
+      priv->data_destroy = data_destroy;
+    }
+}
+
+static inline void
+set_current_filter (GtkRecentAction *action,
+                    GtkRecentFilter *filter)
+{
+  GtkRecentActionPrivate *priv = action->priv;
+
+  g_object_ref (action);
+
+  if (priv->current_filter)
+    g_object_unref (priv->current_filter);
+
+  priv->current_filter = filter;
+
+  if (priv->current_filter)
+    g_object_ref_sink (priv->current_filter);
+
+  g_object_notify (G_OBJECT (action), "filter");
+
+  g_object_unref (action);
+}
+
+static void
+gtk_recent_action_add_filter (GtkRecentChooser *chooser,
+                              GtkRecentFilter  *filter)
+{
+  GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (chooser);
+
+  if (priv->current_filter != filter)
+    set_current_filter (GTK_RECENT_ACTION (chooser), filter);
+}
+
+static void
+gtk_recent_action_remove_filter (GtkRecentChooser *chooser,
+                                 GtkRecentFilter  *filter)
+{
+  GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (chooser);
+
+  if (priv->current_filter == filter)
+    set_current_filter (GTK_RECENT_ACTION (chooser), NULL);
+}
+
+static GSList *
+gtk_recent_action_list_filters (GtkRecentChooser *chooser)
+{
+  GSList *retval = NULL;
+  GtkRecentFilter *current_filter;
+
+  current_filter = GTK_RECENT_ACTION_GET_PRIVATE (chooser)->current_filter;
+  retval = g_slist_prepend (retval, current_filter);
+
+  return retval;
+}
+
+
+static void
+gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
+{
+  iface->set_current_uri = gtk_recent_action_set_current_uri;
+  iface->get_current_uri = gtk_recent_action_get_current_uri;
+  iface->select_uri = gtk_recent_action_select_uri;
+  iface->unselect_uri = gtk_recent_action_unselect_uri;
+  iface->select_all = gtk_recent_action_select_all;
+  iface->unselect_all = gtk_recent_action_unselect_all;
+  iface->get_items = gtk_recent_action_get_items;
+  iface->get_recent_manager = gtk_recent_action_get_recent_manager;
+  iface->set_sort_func = gtk_recent_action_set_sort_func;
+  iface->add_filter = gtk_recent_action_add_filter;
+  iface->remove_filter = gtk_recent_action_remove_filter;
+  iface->list_filters = gtk_recent_action_list_filters;
+}
+
+static void
+gtk_recent_action_activate (GtkAction *action)
+{
+  /* we have probably been invoked by a menu tool button or by a
+   * direct call of gtk_action_activate(); since no item has been
+   * selected, we must unset the current recent chooser pointer
+   */
+  GTK_RECENT_ACTION_GET_PRIVATE (action)->current_chooser = NULL;
+}
+
+static void
+delegate_selection_changed (GtkRecentAction  *action,
+                            GtkRecentChooser *chooser)
+{
+  GtkRecentActionPrivate *priv = action->priv;
+
+  priv->current_chooser = chooser;
+
+  g_signal_emit_by_name (action, "selection-changed");
+}
+
+static void
+delegate_item_activated (GtkRecentAction  *action,
+                         GtkRecentChooser *chooser)
+{
+  GtkRecentActionPrivate *priv = action->priv;
+
+  priv->current_chooser = chooser;
+
+  g_signal_emit_by_name (action, "item-activated");
+}
+
+static void
+gtk_recent_action_connect_proxy (GtkAction *action,
+                                 GtkWidget *widget)
+{
+  GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
+  GtkRecentActionPrivate *priv = recent_action->priv;
+
+  if (GTK_IS_RECENT_CHOOSER (widget) &&
+      !g_slist_find (priv->choosers, widget))
+    {
+      g_object_set (G_OBJECT (widget),
+                    "show-private", priv->show_private,
+                    "show-not-found", priv->show_not_found,
+                    "show-tips", priv->show_tips,
+                    "show-icons", priv->show_icons,
+                    "show-numbers", priv->show_numbers,
+                    "limit", priv->limit,
+                    "sort-type", priv->sort_type,
+                    NULL);
+  
+      if (priv->sort_func)
+        {
+          gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
+                                            priv->sort_func,
+                                            priv->sort_data,
+                                            priv->data_destroy);
+        }
+
+      g_signal_connect_swapped (widget, "selection_changed",
+                                G_CALLBACK (delegate_selection_changed),
+                                action);
+      g_signal_connect_swapped (widget, "item-activated",
+                                G_CALLBACK (delegate_item_activated),
+                                action);
+    }
+
+  GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy (action, widget);
+}
+
+static void
+gtk_recent_action_disconnect_proxy (GtkAction *action,
+                                    GtkWidget *widget)
+{
+  GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
+  GtkRecentActionPrivate *priv = recent_action->priv;
+
+  /* if it was one of the recent choosers we created, remove it
+   * from the list
+   */
+  if (g_slist_find (priv->choosers, widget))
+    priv->choosers = g_slist_remove (priv->choosers, widget);
+
+  GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy (action, widget);
+}
+
+static GtkWidget *
+gtk_recent_action_get_submenu (GtkAction *action)
+{
+  GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
+  GtkRecentActionPrivate *priv = recent_action->priv;
+  GtkWidget *widget;
+
+  widget = g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
+                         "show-private", priv->show_private,
+                         "show-not-found", priv->show_not_found,
+                         "show-tips", priv->show_tips,
+                         "show-icons", priv->show_icons,
+                         "show-numbers", priv->show_numbers,
+                         "limit", priv->limit,
+                         "sort-type", priv->sort_type,
+                         "recent-manager", priv->manager,
+                         NULL);
+  
+  if (priv->sort_func)
+    {
+      gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
+                                        priv->sort_func,
+                                        priv->sort_data,
+                                        priv->data_destroy);
+    }
+
+  g_signal_connect_swapped (widget, "selection_changed",
+                            G_CALLBACK (delegate_selection_changed),
+                            recent_action);
+  g_signal_connect_swapped (widget, "item-activated",
+                            G_CALLBACK (delegate_item_activated),
+                            recent_action);
+
+  /* keep track of the choosers we create */
+  priv->choosers = g_slist_prepend (priv->choosers, widget);
+
+  return widget;
+}
+
+static GtkWidget *
+gtk_recent_action_create_menu_item (GtkAction *action)
+{
+  GtkWidget *menu;
+  GtkWidget *menuitem;
+
+  menu = gtk_recent_action_get_submenu (action);
+  menuitem = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
+  gtk_widget_show (menu);
+
+  return menuitem;
+}
+
+static GtkWidget *
+gtk_recent_action_create_tool_item (GtkAction *action)
+{
+  GtkWidget *menu;
+  GtkWidget *toolitem;
+
+  menu = gtk_recent_action_get_submenu (action);
+  toolitem = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
+  gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (toolitem), menu);
+  gtk_widget_show (menu);
+
+  return toolitem;
+}
+
+static void
+manager_changed_cb (GtkRecentManager *manager,
+                    gpointer          user_data)
+{
+  /* do we need to propagate the signal to all the proxies? guess not */
+}
+
+static void
+set_recent_manager (GtkRecentAction  *action,
+                    GtkRecentManager *manager)
+{
+  GtkRecentActionPrivate *priv = action->priv;
+
+  if (priv->manager)
+    {
+      if (priv->manager_changed_id)
+        {
+          g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+          priv->manager_changed_id = 0;
+        }
+
+      priv->manager = NULL;
+    }
+
+  if (manager)
+    priv->manager = NULL;
+  else
+    priv->manager = gtk_recent_manager_get_default ();
+
+  if (priv->manager)
+    priv->manager_changed_id = g_signal_connect (priv->manager, "changed",
+                                                 G_CALLBACK (manager_changed_cb),
+                                                 action);
+}
+
+static void
+gtk_recent_action_finalize (GObject *gobject)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
+  GtkRecentActionPrivate *priv = action->priv;
+
+  priv->manager = NULL;
+  
+  if (priv->data_destroy)
+    {
+      priv->data_destroy (priv->sort_data);
+      priv->data_destroy = NULL;
+    }
+
+  priv->sort_data = NULL;
+  priv->sort_func = NULL;
+
+  g_slist_free (priv->choosers);
+
+  G_OBJECT_CLASS (gtk_recent_action_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_recent_action_dispose (GObject *gobject)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
+  GtkRecentActionPrivate *priv = action->priv;
+
+  if (priv->manager_changed_id)
+    {
+      if (priv->manager)
+        g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+
+      priv->manager_changed_id = 0;
+    }
+
+  if (priv->current_filter)
+    {
+      g_object_unref (priv->current_filter);
+      priv->current_filter = NULL;
+    }
+
+  G_OBJECT_CLASS (gtk_recent_action_parent_class)->dispose (gobject);
+}
+
+static void
+gtk_recent_action_set_property (GObject      *gobject,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
+  GtkRecentActionPrivate *priv = action->priv;
+
+  switch (prop_id)
+    {
+    case PROP_SHOW_NUMBERS:
+      priv->show_numbers = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      priv->show_private = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      priv->show_not_found = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      priv->show_tips = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      priv->show_icons = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      priv->limit = g_value_get_int (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      priv->local_only = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      priv->sort_type = g_value_get_enum (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      set_current_filter (action, g_value_get_object (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
+                 G_STRFUNC,
+                 G_OBJECT_TYPE_NAME (gobject));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+      set_recent_manager (action, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      return;
+    }
+
+  recent_chooser_set_property (action, pspec->name, value);
+}
+
+static void
+gtk_recent_action_get_property (GObject    *gobject,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_SHOW_NUMBERS:
+      g_value_set_boolean (value, priv->show_numbers);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      g_value_set_boolean (value, priv->show_private);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      g_value_set_boolean (value, priv->show_not_found);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      g_value_set_boolean (value, priv->show_tips);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      g_value_set_boolean (value, priv->show_icons);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      g_value_set_int (value, priv->limit);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      g_value_set_boolean (value, priv->local_only);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      g_value_set_enum (value, priv->sort_type);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      g_value_set_object (value, priv->current_filter);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
+                 G_STRFUNC,
+                 G_OBJECT_TYPE_NAME (gobject));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_recent_action_class_init (GtkRecentActionClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GtkRecentActionPrivate));
+
+  gobject_class->finalize = gtk_recent_action_finalize;
+  gobject_class->dispose = gtk_recent_action_dispose;
+  gobject_class->set_property = gtk_recent_action_set_property;
+  gobject_class->get_property = gtk_recent_action_get_property;
+
+  action_class->activate = gtk_recent_action_activate;
+  action_class->connect_proxy = gtk_recent_action_connect_proxy;
+  action_class->disconnect_proxy = gtk_recent_action_disconnect_proxy;
+  action_class->create_menu_item = gtk_recent_action_create_menu_item;
+  action_class->create_tool_item = gtk_recent_action_create_tool_item;
+  action_class->get_submenu = gtk_recent_action_get_submenu;
+  action_class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
+  action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
+
+  _gtk_recent_chooser_install_properties (gobject_class);
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_SHOW_NUMBERS,
+                                   g_param_spec_boolean ("show-numbers",
+                                                         P_("Show Numbers"),
+                                                         P_("Whether the items should be displayed with a number"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+}
+
+static void
+gtk_recent_action_init (GtkRecentAction *action)
+{
+  GtkRecentActionPrivate *priv;
+
+  action->priv = priv = GTK_RECENT_ACTION_GET_PRIVATE (action);
+
+  priv->show_numbers = FALSE;
+  priv->show_icons = TRUE;
+  priv->show_tips = FALSE;
+  priv->show_not_found = TRUE;
+  priv->show_private = FALSE;
+  priv->local_only = TRUE;
+
+  priv->limit = FALLBACK_ITEM_LIMIT;
+
+  priv->sort_type = GTK_RECENT_SORT_NONE;
+  priv->sort_func = NULL;
+  priv->sort_data = NULL;
+  priv->data_destroy = NULL;
+
+  priv->current_filter = NULL;
+
+  priv->manager = NULL;
+  priv->manager_changed_id = 0;
+}
+
+/**
+ * gtk_recent_action_new:
+ * @name: a unique name for the action
+ * @label: the label displayed in menu items and on buttons
+ * @tooltip: a tooltip for the action
+ * @stock_id: the stock icon to display in widgets representing the action
+ *
+ * Creates a new #GtkRecentAction object. To add the action to
+ * a #GtkActionGroup and set the accelerator for the action,
+ * call gtk_action_group_add_action_with_accel().
+ *
+ * Return value: the newly created #GtkRecentAction.
+ *
+ * Since: 2.12
+ */
+GtkAction *
+gtk_recent_action_new (const gchar *name,
+                       const gchar *label,
+                       const gchar *tooltip,
+                       const gchar *stock_id)
+{
+  return g_object_new (GTK_TYPE_RECENT_ACTION,
+                       "name", name,
+                       "label", label,
+                       "tooltip", tooltip,
+                       "stock-id", stock_id,
+                       NULL);
+}
+
+/**
+ * gtk_recent_action_new_for_manager:
+ * @name: a unique name for the action
+ * @label: the label displayed in menu items and on buttons
+ * @tooltip: a tooltip for the action
+ * @stock_id: the stock icon to display in widgets representing the action
+ * @manager: a #GtkRecentManager or %NULL
+ *
+ * Creates a new #GtkRecentAction object. To add the action to
+ * a #GtkActionGroup and set the accelerator for the action,
+ * call gtk_action_group_add_action_with_accel().
+ *
+ * Return value: the newly created #GtkRecentAction
+ * 
+ * Since: 2.12
+ */
+GtkAction *
+gtk_recent_action_new_for_manager (const gchar      *name,
+                                   const gchar      *label,
+                                   const gchar      *tooltip,
+                                   const gchar      *stock_id,
+                                   GtkRecentManager *manager)
+{
+  return g_object_new (GTK_TYPE_RECENT_ACTION,
+                       "name", name,
+                       "label", label,
+                       "tooltip", tooltip,
+                       "stock-id", stock_id,
+                       "recent-manager", manager,
+                       NULL);
+}
+
+/**
+ * gtk_recent_action_get_show_numbers:
+ * @action: a #GtkRecentAction
+ *
+ * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
+ *
+ * Return value: %TRUE if numbers should be shown.
+ *
+ * Since: 2.12
+ */
+gboolean
+gtk_recent_action_get_show_numbers (GtkRecentAction *action)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_ACTION (action), FALSE);
+
+  return action->priv->show_numbers;
+}
+
+/**
+ * gtk_recent_action_set_show_numbers:
+ * @action: a #GtkRecentAction
+ * @show_numbers
+ *
+ * Sets whether a number should be added to the items shown by the
+ * widgets representing @action. The  numbers are shown to provide
+ * a unique character for a mnemonic to be used inside the menu item's
+ * label. Only the first ten items get a number to avoid clashes.
+ *
+ * Since: 2.12
+ */
+void
+gtk_recent_action_set_show_numbers (GtkRecentAction *action,
+                                    gboolean         show_numbers)
+{
+  GtkRecentActionPrivate *priv;
+
+  g_return_if_fail (GTK_IS_RECENT_ACTION (action));
+
+  priv = action->priv;
+
+  if (priv->show_numbers != show_numbers)
+    {
+      g_object_ref (action);
+
+      priv->show_numbers = show_numbers;
+
+      g_object_notify (G_OBJECT (action), "show-numbers");
+      g_object_unref (action);
+    }
+}
+
+#define __GTK_RECENT_ACTION_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentaction.h b/gtk/gtkrecentaction.h
new file mode 100644 (file)
index 0000000..776f942
--- /dev/null
@@ -0,0 +1,70 @@
+/* GTK - The GIMP Toolkit
+ * Recent chooser action for GtkUIManager
+ *
+ * Copyright (C) 2007, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_ACTION_H__
+#define __GTK_RECENT_ACTION_H__
+
+#include <gtk/gtkaction.h>
+#include <gtk/gtkrecentmanager.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_ACTION                  (gtk_recent_action_get_type ())
+#define GTK_RECENT_ACTION(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_ACTION, GtkRecentAction))
+#define GTK_IS_RECENT_ACTION(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_ACTION))
+#define GTK_RECENT_ACTION_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_ACTION, GtkRecentActionClass))
+#define GTK_IS_RECENT_ACTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_ACTION))
+#define GTK_RECENT_ACTION_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_ACTION, GtkRecentActionClass))
+
+typedef struct _GtkRecentAction         GtkRecentAction;
+typedef struct _GtkRecentActionPrivate  GtkRecentActionPrivate;
+typedef struct _GtkRecentActionClass    GtkRecentActionClass;
+
+struct _GtkRecentAction
+{
+  GtkAction parent_instance;
+  
+  /*< private >*/
+  GtkRecentActionPrivate *priv;
+};
+
+struct _GtkRecentActionClass
+{
+  GtkActionClass parent_class;
+};
+
+GType      gtk_recent_action_get_type         (void) G_GNUC_CONST;
+GtkAction *gtk_recent_action_new              (const gchar      *name,
+                                               const gchar      *label,
+                                               const gchar      *tooltip,
+                                               const gchar      *stock_id);
+GtkAction *gtk_recent_action_new_for_manager  (const gchar      *name,
+                                               const gchar      *label,
+                                               const gchar      *tooltip,
+                                               const gchar      *stock_id,
+                                               GtkRecentManager *manager);
+gboolean   gtk_recent_action_get_show_numbers (GtkRecentAction  *action);
+void       gtk_recent_action_set_show_numbers (GtkRecentAction  *action,
+                                               gboolean          show_numbers);
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_ACTION_H__ */
index bcf06b1c63c199f341e37919dd679a9c1d6ea181..76e7c284b5f12822db51136f9e874469aa281f46 100644 (file)
@@ -2204,8 +2204,9 @@ update_node (GtkUIManager *self,
     case NODE_TYPE_MENU:
       {
        GtkWidget *prev_submenu = NULL;
-       GtkWidget *menu;
+       GtkWidget *menu = NULL;
        GList *siblings;
+
        /* remove the proxy if it is of the wrong type ... */
        if (info->proxy &&  
            G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
@@ -2226,26 +2227,39 @@ update_node (GtkUIManager *self,
            g_object_unref (info->proxy);
            info->proxy = NULL;
          }
+
        /* create proxy if needed ... */
        if (info->proxy == NULL)
          {
-           GtkWidget *tearoff;
-           GtkWidget *filler;
+            /* ... if the action already provides a menu, then use
+             * that menu instead of creating an empty one
+             */
+            if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM &&
+                GTK_ACTION_GET_CLASS (action)->get_submenu)
+              {
+                menu = gtk_action_get_submenu (action);
+              }
+
+            if (!menu)
+              {
+                GtkWidget *tearoff;
+                GtkWidget *filler;
            
-           menu = gtk_menu_new ();
-           gtk_widget_set_name (menu, info->name);
-           tearoff = gtk_tearoff_menu_item_new ();
-           gtk_widget_set_no_show_all (tearoff, TRUE);
-           gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
-           filler = gtk_menu_item_new_with_label (_("Empty"));
-           g_object_set_data (G_OBJECT (filler),
-                              I_("gtk-empty-menu-item"),
-                              GINT_TO_POINTER (TRUE));
-           gtk_widget_set_sensitive (filler, FALSE);
-           gtk_widget_set_no_show_all (filler, TRUE);
-           gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
+                menu = gtk_menu_new ();
+                gtk_widget_set_name (menu, info->name);
+                tearoff = gtk_tearoff_menu_item_new ();
+                gtk_widget_set_no_show_all (tearoff, TRUE);
+                gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
+                filler = gtk_menu_item_new_with_label (_("Empty"));
+                g_object_set_data (G_OBJECT (filler),
+                                   I_("gtk-empty-menu-item"),
+                                   GINT_TO_POINTER (TRUE));
+                gtk_widget_set_sensitive (filler, FALSE);
+                gtk_widget_set_no_show_all (filler, TRUE);
+                gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
+              }
            
-           if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM)
+            if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM)
              {
                info->proxy = menu;
                g_object_ref_sink (info->proxy);
@@ -2284,6 +2298,7 @@ update_node (GtkUIManager *self,
          menu = info->proxy;
        else
          menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
+
        siblings = gtk_container_get_children (GTK_CONTAINER (menu));
        if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
          {
index 4f971c2fdbe4fcd900cad6d3b5d193243293d490..01b6655171d98dae48e7d07ff08876119216da74 100644 (file)
@@ -55,6 +55,19 @@ radio_action (GtkAction *action)
             gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action)));
 }
 
+static void
+recent_action (GtkAction *action)
+{
+  const gchar *name = gtk_action_get_name (action);
+  const gchar *typename = G_OBJECT_TYPE_NAME (action);
+  gchar *uri = gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (action));
+
+  g_message ("Action %s (type=%s) activated (uri=%s)",
+             name, typename,
+             uri ? uri : "no item selected");
+  g_free (uri);
+}
+
 static void
 toggle_cnp_actions (GtkAction *action)
 {
@@ -181,46 +194,49 @@ static const gchar *ui_info =
 "      <menuitem name=\"bold1\" action=\"bold\" />\n"
 "      <menuitem name=\"bold2\" action=\"bold\" />\n"
 "      <separator name=\"sep2\" />\n"
-"      <menuitem name=\"toggle-cnp\" action=\"toggle-cnp\" />\n"
+"      <menuitem name=\"recent\" action=\"recent\" />\n"
 "      <separator name=\"sep3\" />\n"
+"      <menuitem name=\"toggle-cnp\" action=\"toggle-cnp\" />\n"
+"      <separator name=\"sep4\" />\n"
 "      <menuitem name=\"quit\" action=\"quit\" />\n"
 "    </menu>\n"
 "    <menu name=\"Menu _2\" action=\"Menu2Action\">\n"
 "      <menuitem name=\"cut\" action=\"cut\" />\n"
 "      <menuitem name=\"copy\" action=\"copy\" />\n"
 "      <menuitem name=\"paste\" action=\"paste\" />\n"
-"      <separator name=\"sep4\"/>\n"
-"      <menuitem name=\"bold\" action=\"bold\" />\n"
 "      <separator name=\"sep5\"/>\n"
+"      <menuitem name=\"bold\" action=\"bold\" />\n"
+"      <separator name=\"sep6\"/>\n"
 "      <menuitem name=\"justify-left\" action=\"justify-left\" />\n"
 "      <menuitem name=\"justify-center\" action=\"justify-center\" />\n"
 "      <menuitem name=\"justify-right\" action=\"justify-right\" />\n"
 "      <menuitem name=\"justify-fill\" action=\"justify-fill\" />\n"
-"      <separator name=\"sep6\"/>\n"
-"      <menuitem  name=\"customise-accels\" action=\"customise-accels\" />\n"
 "      <separator name=\"sep7\"/>\n"
+"      <menuitem  name=\"customise-accels\" action=\"customise-accels\" />\n"
+"      <separator name=\"sep8\"/>\n"
 "      <menuitem action=\"toolbar-icons\" />\n"
 "      <menuitem action=\"toolbar-text\" />\n"
 "      <menuitem action=\"toolbar-both\" />\n"
 "      <menuitem action=\"toolbar-both-horiz\" />\n"
-"      <separator name=\"sep8\"/>\n"
+"      <separator name=\"sep9\"/>\n"
 "      <menuitem action=\"toolbar-small-icons\" />\n"
 "      <menuitem action=\"toolbar-large-icons\" />\n"
 "    </menu>\n"
-  "    <menu name=\"DynamicMenu\" action=\"Menu3Action\" />\n"
+"    <menu name=\"DynamicMenu\" action=\"Menu3Action\" />\n"
 "  </menubar>\n"
 "  <toolbar name=\"toolbar\">\n"
 "    <toolitem name=\"cut\" action=\"cut\" />\n"
 "    <toolitem name=\"copy\" action=\"copy\" />\n"
 "    <toolitem name=\"paste\" action=\"paste\" />\n"
-"    <separator name=\"sep9\" />\n"
-"    <toolitem name=\"bold\" action=\"bold\" />\n"
+"    <toolitem name=\"recent\" action=\"recent\" />\n"
 "    <separator name=\"sep10\" />\n"
+"    <toolitem name=\"bold\" action=\"bold\" />\n"
+"    <separator name=\"sep11\" />\n"
 "    <toolitem name=\"justify-left\" action=\"justify-left\" />\n"
 "    <toolitem name=\"justify-center\" action=\"justify-center\" />\n"
 "    <toolitem name=\"justify-right\" action=\"justify-right\" />\n"
 "    <toolitem name=\"justify-fill\" action=\"justify-fill\" />\n"
-"    <separator name=\"sep11\"/>\n"
+"    <separator name=\"sep12\"/>\n"
 "    <toolitem name=\"quit\" action=\"quit\" />\n"
 "  </toolbar>\n"
 "  <popup name=\"popup\">\n"
@@ -383,11 +399,23 @@ create_window (GtkActionGroup *action_group)
 int
 main (int argc, char **argv)
 {
+  GtkAction *action;
+
   gtk_init (&argc, &argv);
 
   if (g_file_test ("accels", G_FILE_TEST_IS_REGULAR))
     gtk_accel_map_load ("accels");
 
+  action = gtk_recent_action_new ("recent",
+                                  "Open Recent", "Open recent files",
+                                  NULL);
+  g_signal_connect (action, "item-activated",
+                    G_CALLBACK (recent_action),
+                    NULL);
+  g_signal_connect (action, "activate",
+                    G_CALLBACK (recent_action),
+                    NULL);
+
   action_group = gtk_action_group_new ("TestActions");
   gtk_action_group_add_actions (action_group, 
                                entries, n_entries, 
@@ -403,6 +431,7 @@ main (int argc, char **argv)
                                      toolbar_entries, n_toolbar_entries, 
                                      GTK_TOOLBAR_BOTH,
                                      G_CALLBACK (radio_action), NULL);
+  gtk_action_group_add_action_with_accel (action_group, action, NULL);
 
   create_window (action_group);
 
@@ -422,7 +451,8 @@ main (int argc, char **argv)
       }
   }
 #endif
-
+  
+  g_object_unref (action);
   g_object_unref (action_group);
 
   gtk_accel_map_save ("accels");